package cn.wapache.time.trigger;

import java.io.Serializable;

import cn.wapache.time.calendar.Calendar;
import cn.wapache.timed.Scheduler;

/**
 * The base interface with properties common to all <code>Trigger</code>s
 *
 */
public interface Trigger extends Serializable, Cloneable, Comparable<Trigger> {

    public static final long serialVersionUID = -3904243490805975570L;
    
    public enum TriggerState { CREATE, NORMAL, PAUSED, COMPLETE, ERROR } // NONE, 
    
    public enum CompletedExecutionInstruction {
    	/** 什么都不用做 */
    	NOOP, 
    	/** ??? */
    	RE_EXECUTE_JOB, 
    	///**  */
    	SET_TRIGGER_COMPLETE, 
    	/** 删除Trigger */
    	DELETE_TRIGGER, 
    	/** 对应同一个Job的所有Trigger全部标记为完成. */
        SET_ALL_JOB_TRIGGERS_COMPLETE, 
    	/** 对应同一个Job的所有Trigger全部标记为出错. */
        SET_TRIGGER_ERROR, 
    	/** ??? */
        SET_ALL_JOB_TRIGGERS_ERROR 
    }
    
    public enum MisfirePolicy { 
    	
    	SMART, 
    	
    	IGNORE,
    	
    	DO_NOTHING,
    	
        /**
         * <p>
         * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
         * situation, the <code>{@link SimpleTrigger}</code> wants to be fired
         * now by <code>Scheduler</code>.
         * </p>
         * 
         * <p>
         * <i>NOTE:</i> This instruction should typically only be used for
         * 'one-shot' (non-repeating) Triggers. If it is used on a trigger with a
         * repeat count > 0 then it is equivalent to the instruction <code>{@link #MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT}
         * </code>.
         * </p>
         */
        FIRE_NOW,
        /**
         * <p>
         * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
         * situation, the <code>{@link CalendarIntervalTrigger}</code> wants to be 
         * fired now by <code>Scheduler</code>.
         * </p>
         */
        FIRE_ONCE_NOW,
        
        /**
         * <p>
         * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
         * situation, the <code>{@link SimpleTrigger}</code> wants to be
         * re-scheduled to 'now' (even if the associated <code>{@link Calendar}</code>
         * excludes 'now') with the repeat count left as-is.  This does obey the
         * <code>Trigger</code> end-time however, so if 'now' is after the
         * end-time the <code>Trigger</code> will not fire again.
         * </p>
         * 
         * <p>
         * <i>NOTE:</i> Use of this instruction causes the trigger to 'forget'
         * the start-time and repeat-count that it was originally setup with (this
         * is only an issue if you for some reason wanted to be able to tell what
         * the original values were at some later time).
         * </p>
         */
        RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,
        
        /**
         * <p>
         * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire situation, 
         * 
         * the <code>{@link SimpleTrigger}</code> wants to be re-scheduled to 'now' 
         * (even if the associated <code>{@link Calendar}</code> excludes 'now') with the repeat count set to what it would be, 
         * if it had not missed any firings.  
         * 
         * This does obey the <code>Trigger</code> end-time however, 
         * so if 'now' is after the end-time the <code>Trigger</code> will not fire again.
         * </p>
         * 
         * <p>
         * <i>NOTE:</i> Use of this instruction causes the trigger to 'forget'
         * the start-time and repeat-count that it was originally setup with.
         * Instead, the repeat count on the trigger will be changed to whatever
         * the remaining repeat count is (this is only an issue if you for some
         * reason wanted to be able to tell what the original values were at some
         * later time).
         * </p>
         * 
         * <p>
         * <i>NOTE:</i> This instruction could cause the <code>Trigger</code>
         * to go to the 'COMPLETE' state after firing 'now', if all the
         * repeat-fire-times where missed.
         * </p>
         */
        RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT,
        
        /**
         * <p>
         * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
         * situation, the <code>{@link SimpleTrigger}</code> wants to be
         * re-scheduled to the next scheduled time after 'now' - taking into
         * account any associated <code>{@link Calendar}</code>, and with the
         * repeat count set to what it would be, if it had not missed any firings.
         * </p>
         * 
         * <p>
         * <i>NOTE/WARNING:</i> This instruction could cause the <code>Trigger</code>
         * to go directly to the 'COMPLETE' state if all fire-times where missed.
         * </p>
         */
        RESCHEDULE_NEXT_WITH_REMAINING_COUNT,
        
        /**
         * <p>
         * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
         * situation, the <code>{@link SimpleTrigger}</code> wants to be
         * re-scheduled to the next scheduled time after 'now' - taking into
         * account any associated <code>{@link Calendar}</code>, and with the
         * repeat count left unchanged.
         * </p>
         * 
         * <p>
         * <i>NOTE/WARNING:</i> This instruction could cause the <code>Trigger</code>
         * to go directly to the 'COMPLETE' state if the end-time of the trigger
         * has arrived.
         * </p>
         */
        RESCHEDULE_NEXT_WITH_EXISTING_COUNT
        
    	;
    }

    /**
     * The default value for priority.
     */
    public static final int DEFAULT_PRIORITY = 5;

    public String getId();
    public String getName();
    public String getDescription();
    
    public String getJobId();
    public String getCalendarId();

    public long getStartTime();
    public long getEndTime();
    public long getFireTimeAfter(long afterTime);

    public TriggerState getState();
    public long getPreviousFireTime();
    public long getNextFireTime();
    public long getFinalFireTime();

    public boolean mayFireAgain();
    public MisfirePolicy getMisfireInstruction();
    public int getPriority();
    
    default boolean isEnabled(){
    	switch(this.getState()){
		case CREATE:
		case NORMAL:
			return true;
		case PAUSED:
		case COMPLETE:
		case ERROR:
		default:
			return false;
    	}
    }
    
    default public int compareTo(Trigger other) {
    	
    	if(other==null) {return 1;}
    	
    	long t1 = this.getNextFireTime();
    	long t2 = other.getNextFireTime();
    	
        if (t1>0 || t2>0) {
            if (t1 <= 0 ) { return 1;  }
            if (t2 <= 0 ) { return -1; }
            if (t1 < t2 ) { return -1; }
            if (t1 > t2 ) { return 1;  }
        }

        int comp = other.getPriority() - this.getPriority();
        if (comp != 0) {
            return comp;
        }

        return this.getId().compareTo(other.getId());
    }

    ///////////////////////////////////////////////////////////////////////////
    // Cloneable
    ///////////////////////////////////////////////////////////////////////////
    
    public Object clone();
   
    ///////////////////////////////////////////////////////////////////////////
    // MutableTrigger
    ///////////////////////////////////////////////////////////////////////////
    

	public void setId(String id);
    public void setJobId(String id);
    public void setState(TriggerState state);
    public void setDescription(String description);
    public void setCalendarId(String calendarId);
    public void setPriority(int priority);
    public void setStartTime(long startTime);
    public void setEndTime(long endTime);
    public void setMisfireInstruction(Trigger.MisfirePolicy misfireInstruction);

    public void validate();
    
    public void setNextFireTime(long nextFireTime);
    
    public void setPreviousFireTime(long previousFireTime);

    ///////////////////////////////////////////////////////////////////////////
    // OperableTrigger
    ///////////////////////////////////////////////////////////////////////////
    
    public boolean isInitialized();
    
    public boolean initialize(Calendar calendar);
    
//    /** 初始化, 计算首次触发时间并返回 */
//    public long initialize(Calendar calendar);
//    /** trigger event - fired */
//    public void onFired(Calendar calendar);
//    /** trigger event - calendar changed */
//    public void onCalendarChanged(Calendar cal, long misfireThreshold);
//    
//    public void onMisfire(Calendar cal);
    
    // 将将下面几个方法重命名为上面这几个名字

    /**
     * <p>
     * This method should not be used by the Quartz client.
     * </p>
     * 
     * <p>
     * Called when the <code>{@link Scheduler}</code> has decided to 'fire'
     * the trigger (execute the associated <code>Job</code>), in order to
     * give the <code>Trigger</code> a chance to update itself for its next
     * triggering (if any).
     * </p>
     * 
     * @see #executionComplete(JobExecutionContext, JobExecutionException)
     */
    public void triggered(Calendar calendar);

    /**
     * <p>
     * This method should not be used by the Quartz client.
     * </p>
     * 
     * <p>
     * Called by the scheduler at the time a <code>Trigger</code> is first
     * added to the scheduler, in order to have the <code>Trigger</code>
     * compute its first fire time, based on any associated calendar.
     * </p>
     * 
     * <p>
     * After this method has been called, <code>getNextFireTime()</code>
     * should return a valid answer.
     * </p>
     * 
     * @return the first time at which the <code>Trigger</code> will be fired
     *         by the scheduler, which is also the same value <code>getNextFireTime()</code>
     *         will return (until after the first firing of the <code>Trigger</code>).
     *         </p>
     */
    public long computeFirstFireTime(Calendar calendar);

    /**
     * <p>
     * This method should not be used by the Quartz client.
     * </p>
     * 
     * <p>
     * Called after the <code>{@link Scheduler}</code> has executed the
     * <code>{@link org.quartz.api.job.JobDetail}</code> associated with the <code>Trigger</code>
     * in order to get the final instruction code from the trigger.
     * </p>
     * 
     * @param context
     *          is the <code>JobExecutionContext</code> that was used by the
     *          <code>Job</code>'s<code>execute(xx)</code> method.
     * @param result
     *          is the <code>JobExecutionException</code> thrown by the
     *          <code>Job</code>, if any (may be null).
     * @return one of the <code>CompletedExecutionInstruction</code> constants.
     * 
     * @see CompletedExecutionInstruction
     * @see #triggered(Calendar)
     */
//    public CompletedExecutionInstruction executionComplete(JobExecutionContext context, JobExecutionException result);

    /**
     * <p>
     * This method should not be used by the Quartz client.
     * </p>
     * 
     * <p>
     * To be implemented by the concrete classes that extend this class.
     * </p>
     * 
     * <p>
     * The implementation should update the <code>Trigger</code>'s state
     * based on the MISFIRE_INSTRUCTION_XXX that was selected when the <code>Trigger</code>
     * was created.
     * </p>
     * 
     * @return true: trigger properties changed, false: trigger properties no changed
     * 
     */
    public boolean updateAfterMisfire(Calendar cal);

    /**
     * <p>
     * This method should not be used by the Quartz client.
     * </p>
     * 
     * <p>
     * To be implemented by the concrete class.
     * </p>
     * 
     * <p>
     * The implementation should update the <code>Trigger</code>'s state
     * based on the given new version of the associated <code>Calendar</code>
     * (the state should be updated so that it's next fire time is appropriate
     * given the Calendar's new settings). 
     * </p>
     * 
     * @param cal
     */
    public void updateWithNewCalendar(Calendar cal, long misfireThreshold);
    

    
}
